using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using Deepcode.Flux.Core.Services.XML;

namespace Deepcode.Flux.Core.Systems.CMS.TemplateGallery
{
	/// <summary>
	/// Represents a single known content type definition on the system
	/// Content Type Gallery is held in ~/settings/types and is defined as .type.config
	/// </summary>
	public class ContentType
	{
		#region Private members
		// The ID of the content type
		private string _TypeID = "";
		// The friendly name of the content tyype
		private string _TypeName = "";
		// The maximum number of versions to store for this type (-1 = unlimited - the default)
		private int _LimitVersions = -1;
		// The URL of the Add Form
		private string _AddForm = "";
		// The URL of the Edit Form
		private string _EditForm = "";
		// The URL of the History Form
		private string _HistoryForm = "";
		// The URL of the WFEditForm
		private string _WFEditForm = "";
		// The URL of the WFViewForm
		private string _WFViewForm = "";
		// Indication if the item can be added to CMS root
		private bool _AddRoot = false;
		// List of content type ID's and actions to build relationships from
		private List<ContentTypeRelationshipInfo> _RelationCommands = new List<ContentTypeRelationshipInfo>();
		// Static field data loaded from the XML definition
		private ContentObjectData cod = new ContentObjectData();
		#endregion
		#region Public Properties
		/// <summary>
		/// Holds the ID of the content type definition
		/// </summary>
		public string TypeID { get { return _TypeID; } }
		/// <summary>
		/// Holds the name of the type in the content type definition
		/// </summary>
		public string TypeName { get { return _TypeName; } }
		/// <summary>
		/// Holds the maximum number of versions to store for this type
		/// (overrides global setting). -1 is unlimited and the default.
		/// </summary>
		public int LimitVersions { get { return _LimitVersions; } }
		/// <summary>
		/// Holds the URL of the form used to add this type
		/// </summary>
		public string AddForm { get { return _AddForm; } }
		/// <summary>
		/// Holds the URL of the form used to edit this type
		/// </summary>
		public string EditForm { get { return _EditForm; } }
		/// <summary>
		/// Holds the URL of the form used to preview history entries
		/// </summary>
		public string HistoryForm { get { return _HistoryForm; } }
		/// <summary>
		/// Holds the URL of the form used to edit this data during workflow
		/// </summary>
		public string WFEditForm { get { return _WFEditForm; } }
		/// <summary>
		/// Holds the URL of the form used to view data during workflow
		/// </summary>
		public string WFViewForm { get { return _WFViewForm; } }
		/// <summary>
		/// Determines if the type can be added at the root
		/// </summary>
		public bool AddRoot { get { return _AddRoot; } }
		/// <summary>
		/// Gets the fields content object data associated with the template
		/// </summary>
		public ContentObjectData Fields { get { return this.cod; } }
		#endregion
		#region Constructors
		/// <summary>
		/// Internal use only constructor - loads configuration
		/// from the specified XML file if possible.
		/// </summary>
		/// <param name="XMLFile"></param>
		internal ContentType(string XMLFile)
		{
			LoadFromXML( XMLFile );
		}
		#endregion
		#region Loading utility
		/// <summary>
		/// Internally called function to load info from the XML
		/// </summary>
		/// <param name="XMLFile"></param>
		private void LoadFromXML( string XMLFile )
		{
			Deepcode.Flux.Core.Services.XML.XmlUtilityService xus = new XmlUtilityService();
			
			// Load the XML Document
			XmlDocument typeDoc = new XmlDocument();
			typeDoc.Load(XMLFile);

			// Get rudimentary items
			_TypeID = xus.GetSafe(typeDoc.DocumentElement, "ID", "");
			_TypeName = xus.GetSafe(typeDoc.DocumentElement, "Name", "Untitled type");
			_LimitVersions = xus.GetSafe(typeDoc.DocumentElement, "LimitVersions", -1 );
			_AddForm = xus.GetSafe( typeDoc, "/NodeType/AddForm", "" );
			_EditForm = xus.GetSafe( typeDoc, "/NodeType/EditForm", "" );
			_HistoryForm = xus.GetSafe( typeDoc, "/NodeType/HistoryForm", "" );
			_WFEditForm = xus.GetSafe( typeDoc, "/NodeType/WFEditForm", "" );
			_WFViewForm = xus.GetSafe( typeDoc, "/NodeType/WFViewForm", "" );
			// Get add root relationship
			_AddRoot = xus.GetSafe( typeDoc.SelectSingleNode("/NodeType/Relationships"), "AddRoot", false);
			// Get the relationship ID's for linking up after everything has loaded
			foreach (XmlNode n in xus.GetSafeChildren(typeDoc, "/NodeType/Relationships"))
			{
				if (n.Name == "Deny")
					_RelationCommands.Add( 
						new ContentTypeRelationshipInfo( 
							ContentTypeRelationshipAction.Deny, 
							n.InnerText ));

				if( n.Name == "Add" )
					_RelationCommands.Add(
						new ContentTypeRelationshipInfo(
							ContentTypeRelationshipAction.Add,
							n.InnerText));
			}

			// Get the static content data information
			foreach (XmlNode n in xus.GetSafeChildren(typeDoc, "/NodeType/StaticData"))
				cod.Add( n.Name, n.InnerText );
		}
		#endregion
		#region Relationship tree building
		/// <summary>
		/// Holds the list of things that CAN be added to this
		/// </summary>
		protected List<string> _CanContain = new List<string>();
		/// <summary>
		/// Builds the relationship arrays dictating what can be added to this
		/// and what this can be added to. 
		/// </summary>
		/// <param name="All"></param>
		public void BuildRelationships(List<ContentType> All)
		{
			/*
			 * <ADD> A Particular type (or *) CAN be added in here 
			 * <DENY> A Particular type (or *) CANNOT be added in here
			 */
			foreach (ContentTypeRelationshipInfo Relation in _RelationCommands)
			{
				if (Relation.Action == ContentTypeRelationshipAction.Add)
				{
					// Action requests that a specific type (or all *) can be added to this 
					if (Relation.ContentTypeID == "*")
					{
						foreach( ContentType AllowedChild in All )
							AddAllowedChild( AllowedChild );
					}
					else
					{
						ContentType AllowedChild = Find( All, Relation.ContentTypeID );
						if( AllowedChild != null )
							AddAllowedChild( AllowedChild );
					}
				}
				if (Relation.Action == ContentTypeRelationshipAction.Deny)
				{
					// Action request that a specific type (or all *) NOT be added to this
					if (Relation.ContentTypeID == "*")
					{
						foreach( ContentType DenyChild in All )
							RemoveAllowedChild( DenyChild );
					}
					else
					{
						ContentType DenyChild = Find( All, Relation.ContentTypeID );
						if( DenyChild != null )
							RemoveAllowedChild( DenyChild );
					}
				}
			}
		}
		/// <summary>
		/// Adds a type as an allowed child
		/// </summary>
		/// <param name="AllowedType"></param>
		protected void AddAllowedChild(ContentType AllowedType)
		{
			if (!_CanContain.Contains(AllowedType.TypeID))
				_CanContain.Add(AllowedType.TypeID);
		}
		/// <summary>
		/// Removes the type from the list of allowed types
		/// </summary>
		/// <param name="DenyType"></param>
		protected void RemoveAllowedChild(ContentType DenyType)
		{
			_CanContain.Remove( DenyType.TypeID );
		}
		/// <summary>
		/// Search through All to find the type with the specified ID
		/// </summary>
		/// <param name="All"></param>
		/// <param name="ContentTypeID"></param>
		/// <returns></returns>
		public ContentType Find(List<ContentType> All, string ContentTypeID)
		{
			foreach( ContentType t in All )
				if( t.TypeID == ContentTypeID ) return t;

			return null;
		}
		#endregion
		#region Relationship tree query
		/// <summary>
		/// Determine if the type specified can be added to this type
		/// </summary>
		/// <param name="ContentTypeID"></param>
		/// <returns></returns>
		public bool CanContain(string ContentTypeID)
		{
			if( _CanContain.Contains( ContentTypeID ) ) return true;
			return false;
		}
		/// <summary>
		/// Returns an array of ContentType objects that can be added
		/// as children of this content type.
		/// </summary>
		/// <returns></returns>
		public ContentType[] GetAllowedChildren()
		{
			List<ContentType> children = new List<ContentType>();

			foreach (string allowed in _CanContain)
			{
				ContentType t = ContentTypes.Types.GetByTypeID( allowed );
				if( t != null ) children.Add( t );
			}

			return children.ToArray();
		}
		#endregion
	}
}
